import javax.swing.*;
import javax.swing.plaf.FontUIResource;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashSet;
import java.util.Set;
import java.util.Stack;

import static java.lang.Character.isDigit;

public class Frame extends JFrame {
    private JFrame jFrame=new JFrame("计算器");
    JSplitPane jSplitPane=new JSplitPane(JSplitPane.VERTICAL_SPLIT);
    private JPanel jPanel=new JPanel();
    FontUIResource f = new FontUIResource("楷书", 3, 20);
    int buttonx=80,buttony=50,yy=50;
    String str="";//返回的表达式简写形式
    JTextField jTextField1=new JTextField("",30);
    JTextField jTextField2=new JTextField("键盘输入区域",30);
    JTextField Faults=new JTextField("报错区域",buttonx*5*10);
    int width_T=5*buttonx,height_T=50;
    JTextArea history = new JTextArea();
    String[] strings={
            "sin", "cos", "tan","<=" , "c",
            "√"  , "("  , ")"  , "n!", "÷",
            "x^y", "7"  , "8"  , "9" , "×",
            "ln" , "4"  , "5"  , "6" , "-",
            "log", "1"  , "2"  , "3" , "+",
            "mod", "0"  , "."  , "=" ,"一元多项式计算"
    };
    JButton[] buttons=new JButton[30];
    public void SetButtons(){
        for(int i=0;i<strings.length;i++){
            buttons[i]=new JButton(strings[i]);//可以外加设置字体
            buttons[i].setFont(f);
            buttons[i].setFocusable(false);
        }
        int k=0;
        for(int i = 0; i < 5; i++){
            for (int j = 0; j < 5; j++) {
                buttons[k++].setBounds(buttonx*j,3*height_T+yy*i,buttonx,buttony);
            }
        }
        for(int i=0;i<3;i++){
            buttons[k++].setBounds(buttonx*i,3*height_T+yy*5,buttonx,buttony);
        }
        buttons[k++].setBounds(buttonx*3,3*height_T+yy*5,buttonx*2,buttony);
        buttons[k++].setBounds(0,3*height_T+yy*6,buttonx*2,buttony);

        Faults.setBounds(0,0,width_T,height_T);
        Faults.setText("报错区域");
        Faults.setFont(f);
        Faults.setHorizontalAlignment(JLabel.RIGHT);//文本靠右
        Faults.setEditable(false);//不可修改
        //Faults.setBackground(Color.GRAY);//颜色为灰色

        history.setBounds(0,0,width_T-10,height_T*2-10);
        history.setText("历史记录\n");
        history.setLineWrap(true);//自动换行
        history.setFont(f);
        history.setEditable(false);

        jTextField1.setBounds(0,height_T,width_T,height_T);
        jTextField1.setFont(f);//设置字体的函数
        jTextField1.setHorizontalAlignment(JLabel.RIGHT);//文本靠右
        jTextField1.setEditable(false);//不可修改
        jTextField1.setBackground(Color.WHITE);//颜色为白色

        jTextField2.setBounds(0,height_T*2,width_T,height_T);
        jTextField2.setFont(f);//设置字体的函数
        jTextField2.setHorizontalAlignment(JLabel.RIGHT);//文本靠右
        jTextField2.setEditable(true);//可修改
        jTextField2.setBackground(Color.WHITE);//颜色为白色
    }
    public void initButton(){
        this.SetButtons();
        jPanel.add(Faults);
        jPanel.add(jTextField1);
        jPanel.add(jTextField2);
        for (int i = 0; i < buttons.length; i++) {
            jPanel.add(buttons[i]);
        }
        jPanel.setLayout(null);//自定义设置
    }
    public void buttonListener(){
        int k=0;
        buttons[k++].addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                jTextField1.setText(jTextField1.getText()+"sin");
                str+="s";
            }
        });
        buttons[k++].addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                jTextField1.setText(jTextField1.getText()+"cos");
                str+="c";
            }
        });
        buttons[k++].addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                jTextField1.setText(jTextField1.getText()+"tan");
                str+="t";
            }
        });
        buttons[k++].addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //删除运算符或者操作
                String x=jTextField1.getText();
                if(str.length()>0){
                    char op=str.charAt(str.length()-1);
                    str=str.substring(0,str.length()-1);
                    int sub=1;
                    if("sctom".indexOf(op)>=0)
                        sub=3;
                    else if(op=='l')
                        sub=2;
                    if(x.length()==sub)
                        x="";
                    else
                        x=x.substring(0,x.length()-sub);
                }
                jTextField1.setText(x);
            }
        });
        buttons[k++].addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                str="";
                jTextField1.setText("");
            }
        });
        buttons[k++].addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                jTextField1.setText(jTextField1.getText()+"√");
                str+="g";
            }
        });
        buttons[k++].addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                jTextField1.setText(jTextField1.getText()+"(");
                str+="(";
            }
        });
        buttons[k++].addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                jTextField1.setText(jTextField1.getText()+")");
                str+=")";
            }
        });
        buttons[k++].addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                jTextField1.setText(jTextField1.getText()+"!");
                str+="!";

            }
        });
        buttons[k++].addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                jTextField1.setText(jTextField1.getText()+"÷");
                str+="/";
            }
        });
        buttons[k++].addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                jTextField1.setText(jTextField1.getText()+"^");
                str+="^";
            }
        });
        buttons[k++].addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                jTextField1.setText(jTextField1.getText()+"7");
                str+="7";
            }
        });
        buttons[k++].addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                jTextField1.setText(jTextField1.getText()+"8");
                str+="8";
            }
        });
        buttons[k++].addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                jTextField1.setText(jTextField1.getText()+"9");
                str+="9";
            }
        });
        buttons[k++].addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                jTextField1.setText(jTextField1.getText()+"×");
                str+="*";
            }
        });
        buttons[k++].addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                jTextField1.setText(jTextField1.getText()+"ln");
                str+="l";
            }
        });
        buttons[k++].addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                jTextField1.setText(jTextField1.getText()+"4");
                str+="4";
            }
        });
        buttons[k++].addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                jTextField1.setText(jTextField1.getText()+"5");
                str+="5";
            }
        });
        buttons[k++].addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                jTextField1.setText(jTextField1.getText()+"6");
                str+="6";
            }
        });
        buttons[k++].addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                jTextField1.setText(jTextField1.getText()+"-");
                str+="-";
            }
        });
        buttons[k++].addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                jTextField1.setText(jTextField1.getText()+"log");
                str+="o";
            }
        });
        buttons[k++].addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                jTextField1.setText(jTextField1.getText()+"1");
                str+="1";
            }
        });
        buttons[k++].addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                jTextField1.setText(jTextField1.getText()+"2");
                str+="2";
            }
        });
        buttons[k++].addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                jTextField1.setText(jTextField1.getText()+"3");
                str+="3";
            }
        });
        buttons[k++].addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                jTextField1.setText(jTextField1.getText()+"+");
                str+="+";
            }
        });
        buttons[k++].addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                jTextField1.setText(jTextField1.getText()+"mod");
                str+="m";
            }
        });
        buttons[k++].addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                jTextField1.setText(jTextField1.getText()+"0");
                str+="0";
            }
        });
        buttons[k++].addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                jTextField1.setText(jTextField1.getText()+".");
                str+=".";
            }
        });
        buttons[k++].addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //这里获取答案/打印错误信息
                JTextField selectedTextField = getSelectedTextField(jTextField1, jTextField2);
                if(selectedTextField==jTextField2){
                    String tmp=jTextField2.getText();
                    if(Translate(tmp)){
                        if(Check(simplify)){
                            Faults.setText("报错区域");
                            if(Calc(simplify)){
                                String x=jTextField2.getText();
                                double y=Nums.pop();
                                x=x+"="+y;
                                jTextField2.setText(y+"");
                                history.setText(history.getText()+x+'\n');
                            }
                        }
                        simplify="";
                    }
                }else if(selectedTextField==jTextField1){
                    if(Check(str)){
                        Faults.setText("报错区域");
                        if(Calc(str)){
                            String x=jTextField1.getText();
                            double y=Nums.pop();
                            x=x+"="+y;
                            jTextField1.setText(y+"");
                            history.setText(history.getText()+x+'\n');
                            str=""+y;
                        }
                    }
                }
                Nums.clear();
                operators.clear();
            }
        });
        buttons[k++].addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                Frame_p frameP=new Frame_p();
            }
        });
    }
    String simplify="";
    boolean Translate(String tmp){
        Set<String> count=new HashSet<>();
        count.add("sin");
        count.add("cos");
        count.add("tan");
        count.add("mod");
        count.add("ln");
        count.add("log");
        count.add("sqrt");
        int i=0;
        while(i<tmp.length()){
            while(i<tmp.length()&&"0123456789.+-*/^!()".indexOf(tmp.charAt(i))>=0){
                simplify+=tmp.charAt(i);i++;
            }
            int j=i;
            while(i<tmp.length()&&"0123456789.+-*/^!()".indexOf(tmp.charAt(i))==-1){
                i++;
            }
            if(i==j)
                return true;
            String x=tmp.substring(j,i);
            if(!count.contains(x)){
                Faults.setText("表达式中有错误的运算符");
                simplify="";
                return false;
            }else{
                if(x.equals("log"))simplify+="o";
                else if(x.equals("sqrt"))simplify+="g";
                else simplify+=x.charAt(0);
            }
        }
        return true;
    }
    private static JTextField getSelectedTextField(JTextField textField1, JTextField textField2) {
        if (textField1.hasFocus()) {
            return textField1;
        } else if (textField2.hasFocus()) {
            return textField2;
        } else {
            return null;
        }
    }
    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<>();
        for (int i = 0; i < s.length(); i++) {
            char x = s.charAt(i);
            if(x == '(' || x == '[' || x == '{'){
                stack.push(x);
            }else if(x == ')' || x == ']' || x == '}'){
                if(!stack.empty()){
                    char tmp = stack.peek();
                    if((tmp == '(' && x == ')')||
                            (tmp == '[' && x == ']') ||
                            (tmp == '{' && x == '}') ){
                        stack.pop();
                    }else {
                        return false;
                    }
                }else{
                    return false;
                }
            }
        }
        return stack.empty();
    }
    boolean Check(String str){
        if(str.length()==0){
            Faults.setText("表达式为空");
            return false;
        }
        if(str.length()==1){
            if("0123456789".indexOf(str.charAt(0))==-1){
                Faults.setText("输入错误");
                return false;
            }
        }
        if(str.length()>1){
            if("sctlog0123456789(".indexOf(str.charAt(0))==-1){
                Faults.setText("表达式的第一个字符不能是双目运算符/阶乘");
                return false;
            }
            for(int i=0;i<str.length()-1;i++){
                if("+-*/m".indexOf(str.charAt(i))>=0&&"0123456789losctg(".indexOf(str.charAt(i+1))==-1){
                    Faults.setText("+-*/mod的后面应为数字");
                    return false;
                }
                if(str.charAt(i)=='.'&&"0123456789".indexOf(str.charAt(i+1))==-1){
                    Faults.setText("小数点后面应为数字");
                    return false;
                }
                if(str.charAt(i)=='!'&&"+-*/^".indexOf(str.charAt(i+1))==-1){
                    Faults.setText("阶乘后面不能是数字");
                    return false;
                }
                if("sctlog".indexOf(str.charAt(i))>=0&&"012345679(".indexOf(str.charAt(i+1))==-1){
                    Faults.setText("单目运算符后面应为式子/括号");
                    return false;
                }
                if (str.charAt(0) == '0' && str.charAt(1) == '0'){
                    jTextField1.setText("输入错误！");
                    return false;
                }
                if(i>=1&&str.charAt(i)=='0'){
                    int m=i,n=i,is=0;
                    //1.当0的上一个字符不为0123456789时后一位只能是+-*/.^!
                    if ("0123456789.".indexOf(str.charAt(m - 1)) == -1 && "+-*/.!^)".indexOf(str.charAt(i + 1)) == -1){
                        Faults.setText("0后面应是运算符");
                        return false;
                    }
                    //2.如果0的上一位为“.”,则后一位只能是“0123456789+-*/.^)”中的一个
                    if (str.charAt(m - 1) == '.' && "0123456789+-*/.^)".indexOf(str.charAt(i + 1)) == -1){
                        Faults.setText("小数部分应为数字");
                        return false;
                    }
                    n -= 1;
                    while (n > 0){
                        if ("(+-*/^glosct".indexOf(str.charAt(n)) >= 0){
                            break;
                        }
                        if (str.charAt(n) == '.'){
                            is++;
                        }
                        n--;
                    }
                    //3.如果0到上一个运算符之间没有“.”的话，整数位第一个只能是“123456789”，
                    //  且后一位只能是“0123456789+-*/.!^)”中的一个。
                    if ((is == 0 && str.charAt(n) == '0') || "0123456789+-*/.!^)".indexOf(str.charAt(i + 1)) == -1){
                        Faults.setText("输入错误！");
                        return false;
                    }
                    //4.如果0到上一个运算符之间有一个“.”的话,则后一位只能是“0123456789+-*/.^)”中的一个
                    if (is == 1 && "0123456789+-*/.^)".indexOf(str.charAt(i + 1)) == -1){
                        Faults.setText("小数后面应为恰当的运算符");
                        return false;
                    }
                    if (is > 1){
                        Faults.setText("一个数字中不能包含多个小数点");
                        return false;
                    }
                }
                //7."123456789"后面只能是“0123456789+-*/.!^)”中的一个
                if ("123456789".indexOf(str.charAt(i)) >= 0 && "0123456789+-*/.!^)m".indexOf(str.charAt(i + 1)) == -1){
                    Faults.setText("数字后面应为双目运算符/阶乘");
                    return false;
                }
                //8."("后面只能是“0123456789locstg()ep”中的一个
                if (str.charAt(i) == '(' && "0123456789locstg()".indexOf(str.charAt(i + 1)) == -1){
                    Faults.setText("左括号的后面应为正确的表达式");
                    return false;
                }
                //9.")"后面只能是“+-*/!^)m”中的一个
                if (str.charAt(i) == ')' && "+-*/!^m)".indexOf(str.charAt(i + 1)) == -1){
                    Faults.setText("右括号的后面应为合适的运算符");
                    return false;
                }
                //10.最后一位字符只能是“0123456789!)”中的一个
                if ("0123456789!)".indexOf(str.charAt(str.length() - 1)) == -1){
                    Faults.setText("表达式的结尾应为数字/右括号/阶乘");
                    return false;
                }
                //12.不能有多个“.”
                if (i > 2 && str.charAt(i) == '.'){
                    int n = i - 1;
                    int is = 0;
                    while (n > 0){
                        if ("(+-*/^glosct".indexOf(str.charAt(n)) >= 0){
                            break;
                        }
                        if (str.charAt(n) == '.'){
                            is++;
                        }
                        n--;
                    }
                    if (is > 0){
                        Faults.setText("一个数中不能有多个小数点");
                        return false;
                    }
                }
                if(!isValid(str)){
                    Faults.setText("括号不匹配");
                    return false;
                }
            }
        }
        return true;
    }
    public Frame(){
        this.initButton();
        this.buttonListener();
        jSplitPane.setDividerLocation(height_T*2);
        jSplitPane.add(new JScrollPane(history));
        jSplitPane.add(jPanel);
        jFrame.add(jSplitPane);
        jFrame.setSize(width_T+18,height_T*5+buttony*8);
        jFrame.setLocationRelativeTo(null);//位置为显示器的正中间
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//关闭窗口，程序自动结束
        jFrame.setResizable(false);//大小不能修改
        jFrame.setVisible(true);
    }


    Stack<Character>operators=new Stack<>();//存放运算符（+-*/）
    Stack<Double>Nums=new Stack<>();//存放小数
    //String s="5*5";//测试样例
    int prior(char x){//运算符优先级
        if("tsclog!".indexOf(x)>=0)//tan\sin\cos\log\平方根\ln\阶乘
            return 5;
        else if(x=='^')
            return 4;
        else if(x=='*'||x=='/'||x=='m')
            return 3;
        else if(x=='+'||x=='-')
            return 2;
        else// if(x=='('||x==')')
            return 1;
    }

    boolean Cal_d(){
        char op=operators.pop();
        if(op=='+'||op=='-'||op=='*'||op=='/'||op=='^'||op=='m'){
            double x1=Nums.pop(),x2=Nums.pop();
            switch(op){
                case '+':Nums.add(x1+x2);break;
                case '-':Nums.add(x2-x1);break;
                case '*':Nums.add(x1*x2);break;
                case '^':Nums.add(Math.pow(x2,x1));break;
                case 'm':if(x1>0)Nums.add(x2%x1);
                else{Faults.setText("取模的数要>0");return false;}break;
                case '/':if(x1!=0)Nums.add(x2/x1);
                else {Faults.setText("除数不能为0");return false;}break;//需要特地判断
            }
        }else{
            double x=Nums.pop();
            switch (op){
                case '!':
                    if(x==0||x==1){
                        Nums.add(x);
                    }else if(x==(int)x&&x>1){
                        long d=1;
                        for(long j=(long)x;j>=1;j--)
                            d*=j;
                        Nums.add((double)d);
                    }else{
                        Faults.setText("阶乘应该是自然数");
                        return false;
                    }
                    break;
                case 't':x=x/180*Math.PI;
                    if(Math.tan(x)!=0)
                        Nums.add(Math.tan(x));
                    else{
                        //...tan的x不能为+/-（Π/2+kΠ）
                        Faults.setText("tan的x不能为+/-（Π/2+kΠ）");
                        return false;
                    }
                    break;//tan
                case 's':x=x/180*Math.PI;Nums.add(Math.sin(x));break;//sin
                case 'c':x=x/180*Math.PI;Nums.add(Math.cos(x));break;//cos
                case 'g':if(x>=0)Nums.add(Math.sqrt(x));
                else {Faults.setText("开根的数要>=0");return false;}break;//平方根
                case 'l':if(x>0)Nums.add(Math.log(x));
                else {Faults.setText("ln的数要>=0");return false;}break;//对数ln
                case 'o':if(x>0)Nums.add(Math.log10(x));
                else {Faults.setText("log的数要>=0");return false;}break;//log
            }
        }
        return true;
    }
    public boolean Calc(String s){
        int i=0;
        while(i<s.length()) {
            //将数字从字符中提取出来
            if(isDigit(s.charAt(i))||s.charAt(i)=='.'){
                double sum=0.0;
                while(i<s.length()&&isDigit(s.charAt(i))){
                    sum=sum*10+s.charAt(i)-'0';
                    i++;
                }
                if(i==s.length()){
                    Nums.add(sum);
                    break;
                }
                if(s.charAt(i)=='.'){
                    int j=i++;
                    double x=0.0;
                    while(i<s.length()&&isDigit(s.charAt(i))){
                        x=x*10+s.charAt(i)-'0';
                        i++;
                    }
                    x=x/Math.pow(10,i-j-1);
                    sum+=x;
                }
                Nums.add(sum);
                if(i==s.length())break;
            }

            //运算符的相关操作
            switch (s.charAt(i)) {
                case '(':
                    operators.add(s.charAt(i));
                    break;
                case ')':
                    while (operators.peek() != '(') {
                        if(!Cal_d())
                            return false;
                    }
                    operators.pop();
                    break;
                default:
                    if (operators.empty()) {
                        operators.add(s.charAt(i));
                    } else {
                        while (!operators.empty() && prior(s.charAt(i)) <= prior(operators.peek())) {
                            if(!Cal_d()){
                                return false;
                            }
                        }
                        operators.add(s.charAt(i));
                    }
                    break;
            }
            i++;
        }
        while(!operators.empty()){
            if(!Cal_d())
                return false;
        }
        return true;
    }
    public static void main(String[] args) {
        Frame frameZjs=new Frame();
    }
}